#include <stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#include<signal.h>
#include<unistd.h>

size_t get_file_size(char* filename)
{
    //以只读方式打开
    FILE* fp = fopen(filename,"r");
    if(!fp)
    {
        perror("get_file_size");
        return 0;
    }
    //将文件流地址定位到文件末尾
    fseek(fp,0,SEEK_END);
    if(!fseek)
    {
        perror("SEEK_END");
        return 1;
    }
    //读取当前文件流位置相对于文件头的偏移（即为文件大小）
    long size = ftell(fp);
    fclose(fp);
    return size;
}

//发送文件到客户端
int send_file(char* filename, FILE* fp)
{
char buf[100];
//fprintf(fp,"你好世界！");
//FILE* file = fopen("main.c","r");
//FILE* file = fopen("11.jpg","r");
FILE* file = fopen(filename,"r");
if(!file)
{
    perror("send_file");
    return 1;

}
size_t readn;
while (readn = fread(buf, 1, 100, file))
{
    fwrite(buf, 1, readn, fp);
}
fclose(file);
return 0;
}

//处理客户端请求
//connectfd:客户端连接套接字
//返回值：成功为0，失败为非0值
int handle_client(int connectfd)
{

    //使用标准IO函数与客户端通信，需要将连接套接字与标准IO文件流绑定
    FILE* fp = fdopen(connectfd,"r+");

    if(!fp)
    {
        perror("fdopen");
        return 1;
    }
    char line[80];
    //循环接收客户端的输入
    while (1)
    {

        char method[10];
        char filename[20];
        char protocol[10];
        //读取客户端请求行（第一行），解析文件路径
        fscanf(fp,"%s %s %s\r\n",method,path,protocol);//请求方法、路径、版本号
        printf("filename = %s\n",path);
        //读取客户端发送的请求报文的头部字段，读到空行为止
    while (fgets(line, sizeof (line),fp))
    {

      if (line[0] == '\r')
      {
          break;
      }
      //打印请求消息内容
      //printf(line);
    }
    //判断客户端是否断开连接
    if(ferror(fp))
    {
        printf("client disconnected\n");
        break;
    }

    //给客户端发送响应报文
    size_t size = get_file_size(filename);
    //发送文件到客户端
    //filename: 要发送给客户端的文件
    //fp:客户端

    if(size == 0)
    {
        //文件不存在，返回404
        fprintf(fp,"HTTP/1.1 404 Page not found \r\n" );
        //fprintf(fp,"Content-Type:text/plain;charset=utf-8\r\n");
        fprintf(fp,"Content-Type:text/plain;charset=utf-8\r\n");
        //fprintf(fp,"Content-Length: %d \r\n",get_file_size("main.c"));
        fprintf(fp,"Content-Length: 15 \r\n",size);
        fprintf(fp,"\r\n");
        fprintf(fp,"文件不存在");
    }
    else
    {
    fprintf(fp,"HTTP/1.1 200 OK \r\n" );
    //fprintf(fp,"Content-Type:text/plain;charset=utf-8\r\n");
    fprintf(fp,"Content-Type:image/jpeg;charset=utf-8\r\n");
    //fprintf(fp,"Content-Length: %d \r\n",get_file_size("main.c"));
    fprintf(fp,"Content-Length: %d \r\n",get_file_size("path"));
    fprintf(fp,"\r\n");
    send_file("path",fp);
    }


    //断开客户端的TCP连接
    fclose(fp);
    }
    //return 0;
}

int main()
{
    printf("size = %d\n",get_file_size("filename"));

    //创建监听快捷字
    int listenfd=socket(PF_INET,SOCK_STREAM,0);

    //本程序的IP地址和端口号
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;//IPv4地址（地址类型）
    addr.sin_port = htons(80);//端口号（网络字节序）,如果程序使用小于1024的端口号需要使用管理员权限（root权限）
    addr.sin_addr.s_addr = htonl(INADDR_ANY);//操作系统在所有的地址进行监听，绑定本机的所有IP地址
    //addr.sin_addr.s_addr = inet_addr("192.168.1.111");//指定一个有线或无线网卡的IP地址进行监听

    //将IP地址与监听套接字进行绑定
    int error = bind(listenfd,(struct sockaddr*)&addr,sizeof (addr));
    if(error)
    {
        //perror函数用于打印错误原因
        perror("bind");
        return 1;
    }

    error = listen(listenfd,3);
    if(error)
    {
        perror("listen");
        return 2;
    }
    //忽略SIGPIPE信号，防止发送数据时被OS终止
    signal(SIGPIPE,SIG_IGN);

    //切换根目录，限制服务器只能访问当前路径下的文件
    chroot(".");
    printf("server start\n");
    //循环服务器
    while (1)
    {
    struct sockaddr_in client_addr;
    socklen_t addrlen = sizeof (client_addr);
    //等待客户端连接
    int connectfd = accept(listenfd,(struct sockaddr*)&client_addr,&addrlen);
    if(connectfd == -1)
    {
        perror("accept");
        continue;
    }
    printf("client is connected,ip: %s, port: %d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));

    //开始处理客户端请求
    handle_client(connectfd);
    }

    return 0;
}
